/* eslint-disable no-console */
const express = require('express');
const spdy = require('spdy');
const multer = require('multer');
const fs = require('fs-extra');
const path = require('path');
const {createClient} = require('redis');

const {mergeFile, getFileExtName} = require('./util');

const {
    SERVER_KEY_PATH,
    SERVER_CERT_PATH,
    FILE_FOLDER,
    FILES_HASH_KEY,
    CLIENT_ERROR_CODE,
    SERVER_ERROR_CODE,
    PARTIAL_CONTENT_CODE,
    PORT
} = require('./config');

const upload = multer().any();
const app = express();
app.use(express.json());

const redisClient = createClient();

// 错误处理中间件
const errorHandler = (err, req, res, next) => {
    console.error(err.stack);
    res.status(SERVER_ERROR_CODE).send({msg: err.message});
};

// 计算请求耗时的中间件
const requestTimeMiddleware = (req, res, next) => {
    const startTime = Date.now();

    next(); // 继续传递请求到下一个中间件或路由处理函数

    res.on('close', () => {
        const endTime = Date.now();

        const elapsedTime = endTime - startTime;

        console.log(
            `Request to ${req.method} ${req.url} took ${elapsedTime} ms`
        );
    });
};

app.use(requestTimeMiddleware);

app.use(express.static(__dirname + '/output'));

// 获取上传的文件
app.get('/file/:name', async (req, res) => {
    const file = path.resolve(__dirname, FILE_FOLDER, req.params.name);
    const fileExists = await fs.exists(file);
    if (!fileExists) {
        return res.status(CLIENT_ERROR_CODE).send({msg: '该文件不存在'});
    }
    const readStream = fs.createReadStream(file);
    return readStream.pipe(res);
});

app.post('/speed-test',express.raw({ type: 'application/octet-stream', limit: '1000mb' }), (req, res) => {
    console.log('-------------开始测试----------------', req.body)
    res.send('ok');
})

// 上传分片文件
app.post('/upload', (req, res, next) => {
    upload(req, res, async err => {
        if (err) {
            return next(err);
        }
        const {chunkName, hash, chunkHash} = req.body;
        const exists = await redisClient.sIsMember(hash, chunkHash);
        const filename = path.resolve(__dirname, FILE_FOLDER, chunkName);
        if (exists) {
            const fileExist = await fs.exists(filename);
            if (!fileExist) {
                fs.writeFile(filename, req.files[0].buffer); // 解决不同分片hash相同的问题
            }
            return res.status(PARTIAL_CONTENT_CODE).send({msg: chunkName + '已经存在'});
        }
        
        try {
            await Promise.all([
                fs.writeFile(filename, req.files[0].buffer),
                redisClient.sAdd(hash, chunkHash)
            ]);
        } catch (err) {
            return next(err);
        }

        return res.status(PARTIAL_CONTENT_CODE).send(chunkName + '上传完毕');
    });
});

// 合并文件
app.post('/merge', async (req, res, next) => {
    const {filename, length, hash} = req.body;
    const extName = getFileExtName(filename);

    const exists = await redisClient.sIsMember(FILES_HASH_KEY, hash);
    if (exists) {
        return res.status(CLIENT_ERROR_CODE).send({
            msg: '文件已存在！',
            fileURL: `/file/${hash}.${extName}`
        });
    }
    try {
        await mergeFile({filename, length, hash, outputFolder: FILE_FOLDER});
        await Promise.all([
            redisClient.del(hash), // 删除所有分片hash
            redisClient.sAdd(FILES_HASH_KEY, hash) // 存储已上传的文件hash
        ]);
    } catch (err) {
        return next(err);
    }

    return res.send({fileURL: `/file/${hash}.${extName}`});
});

// 上传前根据hash check改文件是否存在
app.post('/check', async (req, res) => {
    // 目录不存在，创建目录
    const dirExists = await fs.pathExists(path.resolve(__dirname, FILE_FOLDER));
    if (!dirExists) {
        fs.mkdir(path.resolve(__dirname, FILE_FOLDER));
    }
    const {hash, fileName} = req.body;
    const exits = await redisClient.sIsMember(FILES_HASH_KEY, hash);
    if (exits) {
        const extName = getFileExtName(fileName);
        return res.send({
            fileURL: `/file/${hash}.${extName}`,
            exits
        });
    }
    return res.send({exits});
});

// 连接redis
(async () => {
    await redisClient.connect();
    redisClient.on('error', err => console.log('Redis Client Error', err));
})();

const server = spdy.createServer(
    {
        key: fs.readFileSync(SERVER_KEY_PATH),
        cert: fs.readFileSync(SERVER_CERT_PATH)
    },
    app
);

function bindingToPort(port) {
    server.listen(port, err => {
        if (err) {
            console.log('error on binding port: ' + port);
            return;
        }
        console.log(`Server is running on https://localhost:${port}`);
    });
    server.on('error', err => {
        if (err.code === 'EADDRINUSE') {
            server.close(() => {
                bindingToPort(port + 1);
            });
        } else {
            console.log('error ', err);
        }
    });
}

app.use(errorHandler);

// 启动服务
bindingToPort(PORT);
